package com.wzx.demo.conf;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * 用户配置
 *
 * @author wangzhengxing
 * @version 1.0
 * @Date 2019-10-31 09:07
 */
@Configuration
public class RabbitConfiguration {

    //===========================================Direct模式====================================
    /**
     * 配置路由交换对象实例
     * @return
     */
    @Bean
    public DirectExchange directExchange() {
        return new DirectExchange((ExchangeEnum.DIRECT_EXCHANGE.getExchangeName()));
    }

    /**
     * 配置注册队列实例并设置持久化队列
     * @return
     */
    @Bean
    public Queue directQueue() {
        return new Queue(QueueEnum.DIRECT_QUEUE.getQueueName(), true);
    }

    /**
     * 注册队列绑定到路由交换配置上并设置指定路由键进行转发
     * @return
     */
    @Bean
    public Binding directBinding() {
        return BindingBuilder.bind(directQueue()).to(directExchange()).with(QueueEnum.DIRECT_QUEUE.getRoutingKey());
    }

    //================================================死信队列===============================================
    /**
     * 所谓死信：即（1）消息被拒绝（basic.reject 或者 basic.nack），并且requeue=false;（2）消息的过期时间到期了；
     *      * （3）队列长度限制超过了 等三个因素造成。
     *      * 我们会将以上原因造成的队列存入死信队列，死信队列其实也是一个普通的队列，我们可以根据自身需要，可以对死信进行操作。
     *      * 以下为死信队列的演示（将正常队列监听关闭并设置超时）：首先声明一个正常的队列，并设置死信队列的相关声明【死信交换器（与正常队列一致即可），死信路由Key等】
     *      * 设置完后，准备一个新的队列，此队列用于接收上一个正常队列发生死信后，将由此队列代替（即候补队列），然后将新队列通过上一个交换器以及正常队列中声明的死信路由Key进行绑定
     *      * 该操作与正常声明一致（声明交换器（可使用正常队列的交换器，无需另外声明），队列，将队列绑定到交换器）
     */
    /**
     * 直连死信交换器，正常与死信交换器一致
     * @return
     */
    @Bean
    public DirectExchange directDeadExchange() {
        return new DirectExchange(ExchangeEnum.DIRECT_DEAD_EXCHANGE.getExchangeName(), true, false);
    }

    /**
     * 声明一个正常的队列，并设置死信相关信息（交换器，路由Key），确保发生死信后会将死信存入交换器
     * @return
     */
    @Bean
    public Queue directDeadQueue() {
        Map<String, Object> args = new HashMap<>(4);
        // x-dead-letter-exchange 声明 死信交换机
        args.put("x-dead-letter-exchange", ExchangeEnum.DIRECT_DEAD_EXCHANGE.getExchangeName());
        // x-dead-letter-routing-key 声明死信路由键
        args.put("x-dead-letter-routing-key", QueueEnum.DEAD_QUEUE.getRoutingKey());
        return new Queue(QueueEnum.DIRECT_DEAD_QUEUE.getQueueName(), true, false, false, args);
    }

    /**
     * 将队列绑定到指定交换器并设置路由
     */
    @Bean
    public Binding directDeadBinding() {
        return BindingBuilder.bind(directDeadQueue()).to(directDeadExchange()).with(QueueEnum.DIRECT_DEAD_QUEUE.getRoutingKey());
    }

    /**
     * 死信队列（候补队列） 若上面的正常队列发生死信时，需将发生死信的队列信息路由到此队列中
     * 路由过程：正常队列发送->信息到交换器->交换器路由到正常队列->监听,发生死信->死信回到指定的交换器->再由交换器路由到死信队列->死信监听
     */
    @Bean
    public Queue deadQueue() {
        return new Queue(QueueEnum.DEAD_QUEUE.getQueueName(), true, false, false);
    }

    /**
     * 绑定死信的队列到候补队列
     */
    @Bean
    public Binding deadBinding() {
        return BindingBuilder.bind(deadQueue()).to(directDeadExchange()).with(QueueEnum.DEAD_QUEUE.getRoutingKey());
    }


    //======================================================Fanout模式=====================================
    /**
     * 广播路由
     * @return
     */
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange(ExchangeEnum.FANOUT_EXCHANGE.getExchangeName());
    }

    /**
     * 广播队列
     * @return
     */
    @Bean
    public Queue fanoutQueue1() {
        return new Queue(QueueEnum.FANOUT_QUEUE.getQueueName(), true);
    }

    /**
     * 绑定广播队列到广播路由
     * @return
     */
    @Bean
    public Binding fanoutBinding() {
        return BindingBuilder.bind(fanoutQueue1()).to(fanoutExchange());
    }

    //===================================================Topic模式=================================
    /**
     * 主题路由
     * @return
     */
    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange(ExchangeEnum.TOPIC_EXCHANGE.getExchangeName());
    }

    /**
     * 主题队列
     * @return
     */
    @Bean
    public Queue topicQueue() {
        return new Queue(QueueEnum.TOPIC_QUEUE.getQueueName(), true);
    }

    /**
     * 绑定主题队列到路由
     * @return
     */
    @Bean
    public Binding topicBinding() {
        return BindingBuilder.bind(topicQueue()).to(topicExchange()).with(QueueEnum.TOPIC_QUEUE.getRoutingKey());
    }
}
